from django.db import models
from django.contrib.auth.models import User
from fiteat.middleware import threadlocals
from fiteat.utils import get_units, truncate, flatten, save_with_trends, Constants, K
from django.db.models.options import AdminOptions
from django.core import validators
from django.utils.translation import gettext_lazy as _
from decimal import Decimal
import re
from fiteat.apps.main.models import Profile

weight_fields = ['weight']
length_fields = ['chest', 'waist', 'hips', 'neck', 'biceps', 'arm', 'forearm', 'calf']
numeric_fields = ['bf_user']
custom_fields = ['custom1', 'custom2', 'custom3', 'custom4', 'custom5']

text_fields = ['comment']

have_trend_fields = weight_fields + length_fields + numeric_fields
numeric_trend_fields = flatten([(x, '%s_trend' % x) for x in numeric_fields])
weight_trend_fields = flatten([(x, '%s_trend' % x) for x in weight_fields])
length_trend_fields = flatten([(x, '%s_trend' % x) for x in length_fields])
custom_trend_fields = flatten([(x, '%s_trend' % x) for x in custom_fields])
all_trend_fields = weight_trend_fields + numeric_trend_fields + length_trend_fields + custom_trend_fields

def latest_field(field):
    def f(): 
        try:
            return getattr(Measurement.objects.latest('date'), field)
        except Measurement.DoesNotExist:
            return None
    return f

class Measurement(models.Model):
    user = models.ForeignKey(User, related_name='owner', blank=True, editable=False)
    date = models.DateField(_('date'))
    weight = models.DecimalField(_('weight'), max_digits=5, decimal_places=2, default=latest_field('weight'))
    weight_trend = models.DecimalField(_('weight\'s trend'), max_digits=5, decimal_places=2, blank=True, null=True, editable=False)
    # The Body Fat % measurement made by the user (e.g., using calipers
    bf_user = models.DecimalField(_('User provided BF%'), max_digits=3, decimal_places=1, blank=True, null=True, default=latest_field('bf_user'))
    bf_user_trend = models.DecimalField(_('User provided BF%\'s trend'), max_digits=3, decimal_places=1, blank=True, null=True)
    chest = models.DecimalField(_('chest'), max_digits=4, decimal_places=1, blank=True, null=True, default=latest_field('chest'))
    chest_trend = models.DecimalField(_('chest\'s trend'), max_digits=4, decimal_places=1, blank=True, null=True)
    waist = models.DecimalField(_('waist'), max_digits=4, decimal_places=1, blank=True, null=True, default=latest_field('waist'))
    waist_trend = models.DecimalField(_('waist\'s trend'), max_digits=4, decimal_places=1, blank=True, null=True)
    hips = models.DecimalField(_('hips'), max_digits=4, decimal_places=1, blank=True, null=True, default=latest_field('hips'))
    hips_trend = models.DecimalField(_('hips\' trend'), max_digits=4, decimal_places=1, blank=True, null=True)
    neck = models.DecimalField(_('neck'), max_digits=3, decimal_places=1, blank=True, null=True, default=latest_field('neck'))
    neck_trend = models.DecimalField(_('neck\'s trend'), max_digits=3, decimal_places=1, blank=True, null=True)
    biceps = models.DecimalField(_('biceps'), max_digits=3, decimal_places=1, blank=True, null=True, default=latest_field('biceps'))
    biceps_trend = models.DecimalField(_('biceps\' trend'), max_digits=3, decimal_places=1, blank=True, null=True)
    arm = models.DecimalField(_('arm'), max_digits=3, decimal_places=1, blank=True, null=True, default=latest_field('arm'))
    arm_trend = models.DecimalField(_('arm\'s trend'), max_digits=3, decimal_places=1, blank=True, null=True)
    forearm = models.DecimalField(_('forearm'), max_digits=3, decimal_places=1, blank=True, null=True, default=latest_field('forearm'))
    forearm_trend = models.DecimalField(_('forearm\'s trend'), max_digits=3, decimal_places=1, blank=True, null=True)
    calf = models.DecimalField(_('calf'), max_digits=3, decimal_places=1, blank=True, null=True, default=latest_field('calf'))
    calf_trend = models.DecimalField(_('calf\'s trend'), max_digits=3, decimal_places=1, blank=True, null=True)
    comment = models.CharField(_('comment'), max_length=200, blank=True, null=True)
    custom1 = models.DecimalField(_('custom1'), max_digits=5, decimal_places=2, blank=True, null=True)
    custom1_trend = models.DecimalField(_('custom1\'s trend'), max_digits=5, decimal_places=2, blank=True, null=True)
    custom2 = models.DecimalField(_('custom2'), max_digits=5, decimal_places=2, blank=True, null=True)
    custom2_trend = models.DecimalField(_('custom2\'s trend'), max_digits=5, decimal_places=2, blank=True, null=True)
    custom3 = models.DecimalField(_('custom3'), max_digits=5, decimal_places=2, blank=True, null=True)
    custom3_trend = models.DecimalField(_('custom3\'s trend'), max_digits=5, decimal_places=2, blank=True, null=True)
    custom4 = models.DecimalField(_('custom4'), max_digits=5, decimal_places=2, blank=True, null=True)
    custom4_trend = models.DecimalField(_('custom4\'s trend'), max_digits=5, decimal_places=2, blank=True, null=True)
    custom5 = models.DecimalField(_('custom5'), max_digits=5, decimal_places=2, blank=True, null=True)
    custom5_trend = models.DecimalField(_('custom5\'s trend'), max_digits=5, decimal_places=2, blank=True, null=True)

    def __unicode__(self):
        return _("your measurements for %(date)s") % ({'date': self.date})

    # The Body Fat % measurement calculated from the various circumferences using a formula
    def bf_calc(self):
        return 0

    bf_calc.short_description = _('Calculated BF%')
        
    def save(self):
        save_with_trends(self, Measurement, have_trend_fields)

    class Admin:
        date_hierarchy = 'date'
        list_display = ('date',)
        ordering = ('-date',)
        fields = (
            (None, {
                'fields': ('date', 'weight', 'bf_user', 'comment')
            }),
            ('More measurements', {
          #      'classes': 'collapse',
                'fields': length_fields + custom_fields
            })
        )

    class Meta:
        verbose_name = _('measurement')
        verbose_name_plural = _('measurements')

UNIT_CHOICES = (
    ('M', _('Metric')),
    ('E', _('English'))
)

FIELD_CHOICES = (
    ('weight', _('Weight')),
    ('bf_user', _('BF %')),
    ('chest', _('Chest')),
    ('waist', _('Waist')),
    ('hips', _('Hips')),
    ('neck', _('Neck')),
    ('biceps', _('Biceps')),
    ('arm', _('Arm')),
    ('forearm', _('Forearm')),
    ('calf', _('Calf')),
    ('custom1', _('Custom1')),
    ('custom2', _('Custom2')),
    ('custom3', _('Custom3')),
    ('custom4', _('Custom4')),
    ('custom5', _('Custom5')),
)

class UserPref(models.Model):
    user = models.ForeignKey(User, related_name='mup_owner', blank=True, editable=False, unique=True)
    profile = models.ForeignKey(Profile, edit_inline=models.STACKED, unique=True, num_in_admin=1, related_name="measurements_profile", max_num_in_admin=1)
    # TBD: keep the weight in the DB always in kg?
    weight = models.BooleanField(_('weight'), default=True, core=True)
    weight_trend = models.BooleanField(_('weight\'s trend'), default=True)
    # The Body Fat % measurement made by the user (e.g., using calipers
    bf_user = models.BooleanField(_('User provided BF%'), default=False)
    bf_user_trend = models.BooleanField(_('User provided BF%\'s trend'), default=False)
    # The Body Fat % measurement calculated from the various circumferences using a formula
    bf_calc = models.BooleanField(_('Calculated BF%'), default=False)
    chest = models.BooleanField(_('chest'), default=False)
    chest_trend = models.BooleanField(_('chest\'s trend'), default=False)
    waist = models.BooleanField(_('waist'), default=False)
    waist_trend = models.BooleanField(_('waist\'s trend'), default=False)
    hips = models.BooleanField(_('hips'), default=False)
    hips_trend = models.BooleanField(_('hips\' trend'), default=False)
    neck = models.BooleanField(_('neck'), default=False)
    neck_trend = models.BooleanField(_('neck\'s trend'), default=False)
    biceps = models.BooleanField(_('biceps'), default=False)
    biceps_trend = models.BooleanField(_('biceps\' trend'), default=False)
    arm = models.BooleanField(_('arm'), default=False)
    arm_trend = models.BooleanField(_('arm\'s trend'), default=False)
    forearm = models.BooleanField(_('forearm'), default=False)
    forearm_trend = models.BooleanField(_('forearm\'s trend'), default=False)
    calf = models.BooleanField(_('calf'), default=False)
    calf_trend = models.BooleanField(_('calf\'s trend'), default=False)
    comment = models.BooleanField(_('comment'), default=False)
    custom1 = models.BooleanField(_('custom1'), default=False)
    custom1_trend = models.BooleanField(_('custom1\'s trend'), default=False)
    custom2 = models.BooleanField(_('custom2'), default=False)
    custom2_trend = models.BooleanField(_('custom2\'s trend'), default=False)
    custom3 = models.BooleanField(_('custom3'), default=False)
    custom3_trend = models.BooleanField(_('custom3\'s trend'), default=False)
    custom4 = models.BooleanField(_('custom4'), default=False)
    custom4_trend = models.BooleanField(_('custom4\'s trend'), default=False)
    custom5 = models.BooleanField(_('custom5'), default=False)
    custom5_trend = models.BooleanField(_('custom5\'s trend'), default=False)
    custom1_caption = models.CharField(_('custom1 caption'), max_length=30, blank=True, validator_list=[validators.RequiredIfOtherFieldGiven('custom1')])
    custom2_caption = models.CharField(_('custom2 caption'), max_length=30, blank=True, validator_list=[validators.RequiredIfOtherFieldGiven('custom2')])
    custom3_caption = models.CharField(_('custom3 caption'), max_length=30, blank=True, validator_list=[validators.RequiredIfOtherFieldGiven('custom3')])
    custom4_caption = models.CharField(_('custom4 caption'), max_length=30, blank=True, validator_list=[validators.RequiredIfOtherFieldGiven('custom4')])
    custom5_caption = models.CharField(_('custom5 caption'), max_length=30, blank=True, validator_list=[validators.RequiredIfOtherFieldGiven('custom5')])

    weight_goal = models.DecimalField(_('weight goal'), max_digits=5, decimal_places=2)
    bf_user_goal = models.DecimalField("User provided BF% goal", max_digits=3, decimal_places=1, blank=True, null=True)
    chest_goal = models.DecimalField(_('chest goal'), max_digits=4, decimal_places=1, blank=True, null=True)
    waist_goal = models.DecimalField(_('waist goal'), max_digits=4, decimal_places=1, blank=True, null=True)
    hips_goal = models.DecimalField(_('hips goal'), max_digits=4, decimal_places=1, blank=True, null=True)
    neck_goal = models.DecimalField(_('neck goal'), max_digits=3, decimal_places=1, blank=True, null=True)
    biceps_goal = models.DecimalField(_('biceps goal'), max_digits=3, decimal_places=1, blank=True, null=True)
    arm_goal = models.DecimalField(_('arm goal'), max_digits=3, decimal_places=1, blank=True, null=True)
    forearm_goal = models.DecimalField(_('forearm goal'), max_digits=3, decimal_places=1, blank=True, null=True)
    calf_goal = models.DecimalField(_('calf goal'), max_digits=3, decimal_places=1, blank=True, null=True)
    custom1_goal = models.DecimalField(_('custom1 goal'), max_digits=5, decimal_places=2, blank=True, null=True)
    custom2_goal = models.DecimalField(_('custom2 goal'), max_digits=5, decimal_places=2, blank=True, null=True)
    custom3_goal = models.DecimalField(_('custom3 goal'), max_digits=5, decimal_places=2, blank=True, null=True)
    custom4_goal = models.DecimalField(_('custom4 goal'), max_digits=5, decimal_places=2, blank=True, null=True)
    custom5_goal = models.DecimalField(_('custom5 goal'), max_digits=5, decimal_places=2, blank=True, null=True)

    custom_chart1_field1 = models.CharField(_("Field #1"), max_length=30, choices=FIELD_CHOICES, blank=True, null=True)
    custom_chart1_field2 = models.CharField(_("Field #2"), max_length=30, choices=FIELD_CHOICES, blank=True, null=True)
    custom_chart1_field3 = models.CharField(_("Field #3"), max_length=30, choices=FIELD_CHOICES, blank=True, null=True)

    custom_chart2_field1 = models.CharField(_("Field #1"), max_length=30, choices=FIELD_CHOICES, blank=True, null=True)
    custom_chart2_field2 = models.CharField(_("Field #2"), max_length=30, choices=FIELD_CHOICES, blank=True, null=True)
    custom_chart2_field3 = models.CharField(_("Field #3"), max_length=30, choices=FIELD_CHOICES, blank=True, null=True)

    custom_chart3_field1 = models.CharField(_("Field #1"), max_length=30, choices=FIELD_CHOICES, blank=True, null=True)
    custom_chart3_field2 = models.CharField(_("Field #2"), max_length=30, choices=FIELD_CHOICES, blank=True, null=True)
    custom_chart3_field3 = models.CharField(_("Field #3"), max_length=30, choices=FIELD_CHOICES, blank=True, null=True)

    def __unicode__(self):
        return str(_("Your measurements settings"))

    def save(self):
        self.user = threadlocals.get_current_user()
        super(UserPref,self).save()

    class Admin:
        fields = (
            (_('The units system'), {
                'fields': ('units_system',)
            }),
            (_('Fields to show in the tabular view'), {
                'fields': ('weight', 'weight_trend', 'bf_user', 'bf_user_trend', 'bf_calc', 'chest', 'chest_trend', 'waist', 'waist_trend', 'hips', 'hips_trend', 'neck', 'neck_trend', 'biceps', 'biceps_trend', 'arm', 'arm_trend', 'forearm', 'forearm_trend', 'calf', 'calf_trend',  'comment', 'custom1', 'custom1_trend', 'custom2', 'custom2_trend', 'custom3', 'custom3_trend', 'custom4', 'custom4_trend', 'custom5', 'custom5_trend',)
            }),
            (_('Captions of the custom fields'), {
                'classes': 'collapse',
                'fields': ('custom1_caption','custom2_caption','custom3_caption','custom4_caption', 'custom5_caption', )
            }),
            (_('Goals'), {
                'classes': 'collapse',
                'fields': ('weight_goal', 'bf_user_goal', 'chest_goal', 'waist_goal', 'hips_goal', 'neck_goal', 'biceps_goal', 'arm_goal', 'forearm_goal', 'calf_goal', 'custom1_goal', 'custom2_goal', 'custom3_goal', 'custom4_goal', 'custom5_goal')
            }),
            (_('Custom chart #1'), {
                'classes': 'collapse',
                'fields': ('custom_chart1_field1', 'custom_chart1_field2', 'custom_chart1_field3',)
            }),
            (_('Custom chart #2'), {
                'classes': 'collapse',
                'fields': ('custom_chart2_field1', 'custom_chart2_field2', 'custom_chart2_field3', )
            }),
            (_('Custom chart #3'), {
                'classes': 'collapse',
                'fields': ('custom_chart3_field1', 'custom_chart3_field2', 'custom_chart3_field3', )
            }),
        )

    class Meta:
        verbose_name = _('measurement related settings')
        verbose_name_plural = _('measurement related settings')


class ApplicationAdminOptions(AdminOptions):
    def _list_display(self):
        user = threadlocals.get_current_user()
        if user is None or user.is_anonymous():
            return ()

        fields = ['date',]
        prefs = user.get_profile().measurements_profile
        weight, length, dist = get_units(user.get_profile())
        for i in Measurement._meta.fields:
            if i.name in weight_trend_fields:
                i.verbose_name = '%s (%s)' % (re.sub('_', ' ', i.name.title()), weight)
            if i.name in length_trend_fields:
                i.verbose_name = '%s (%s)' % (re.sub('_', ' ', i.name.title()), length)
            if re.match('custom\d$', i.name):
                i.verbose_name = eval('prefs.%s_caption' % i.name)
            m = re.match('(?P<name>custom\d)_trend$', i.name)
            if m:
                i.verbose_name = eval('prefs.%s_caption' % m.group('name')) + "'s trend"
# TODO: Add caption for the custom fields' trend
        for attr in all_trend_fields + ['bf_calc', 'comment']:
            if eval('prefs.%s' % attr):
                fields.append(attr)
        return fields
    list_display = property(_list_display)

del Measurement._meta.admin.list_display
Measurement._meta.admin.__class__ = ApplicationAdminOptions

